using System;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using My.IdlConverter.Core;
using My.IdlConverter.Core.Producers;
using My.IdlConverter.Core.Filters;
using My.IdlConverter.Core.Searchers;
using My.IdlConverter.Core.Util;
using My.IdlConverter.Core.Types;

namespace My.IdlConverter.Com
{
    public class ComClassCppFileProducer : ClassProducer
    {
        private log4net.ILog log = log4net.LogManager.GetLogger(typeof(ComClassCppFileProducer));
        public static readonly string HEADER_METHOD_REGEX = @".*?\);";

        public ComClassCppFileProducer()
        {
        }
        public ComClassCppFileProducer(Class consumer)
            : base(consumer)
        {
        }

        public override string Produce()
        {
            ComClassHeaderFileProducer headerFileProducer = new ComClassHeaderFileProducer(Consumer);
            string headerFileString = headerFileProducer.GetMethodsString(Consumer).ToString();

            StringBuilder result = new StringBuilder();
            IList<Method> methods = Consumer.Methods;
            string className = Consumer.Name;

            result.AppendLine("#include \"stdafx.h\"");
            result.AppendLine();
            result.AppendLine(GlobalConst.FileIncludeTemplate.Replace(GlobalConst.TemplatePattern, ClassModifier.GetTitle(Consumer)));
            result.AppendLine();

            Regex reg = new Regex(HEADER_METHOD_REGEX);
            MatchCollection matchCol = reg.Matches(headerFileString);
            int i = 0;
            //result.AppendLine("/*");
            foreach (Match nextMatch in matchCol)
            {
                result.Append(GetMethodImp(methods[i++], nextMatch.Value, className));
                result.AppendLine();
            }
            //result.AppendLine("*/");

            return result.ToString();
        }

        private string GetMethodImp(Method theMethod, string methodString,string className)
        {
            className = ClassModifier.GetName(className);
            methodString = methodString.Trim(new char[] { ' ','\t'});
            EliminateColon(ref methodString);

            StringBuilder sb = new StringBuilder();

            int pos = methodString.IndexOf('(');
            int beg = 0;
            string methodDecr = methodString.Substring(beg, pos - beg);
            int methodDecrPos = methodDecr.LastIndexOf(' ');
            methodDecr = methodString.Substring(methodDecrPos+1, pos - methodDecrPos  - 1);
            string temp = methodString.Remove(beg, pos - beg);
            temp = temp.Trim();
            temp = methodDecr + temp;
            sb.AppendFormat("STDMETHODIMP {0}::{1}", className, temp);
            sb.AppendLine();

            sb.AppendLine("{");
            sb.AppendLine("/*");
            sb.Append(GetImplementation(theMethod));
            sb.AppendLine("*/");
            sb.AppendLine("}");

            return sb.ToString();
        }

        private bool IsConstructor(string methodString)
        {
            return (methodString.IndexOf("HRESULT") == -1);
        }

        private void EliminateColon(ref string methodString)
        {
            int pos = methodString.LastIndexOf(";");
            Debug.Assert(pos != -1);
            methodString = methodString.Remove(pos,1);
        }

        private string GetImplementation(Method theMethod)
        {
            StringBuilder sb = new StringBuilder();

            Interface bloodParent = Searcher.SearchBloodParent(theMethod);
            string interfaceVar = "";
            if (bloodParent != null)
            {
                interfaceVar = "pIFDOM" + InterfaceModifier.GetTitle(bloodParent.Name);
                sb.AppendLine(StringUtil.FormatIndent1("{0}* {1} = NULL;", bloodParent.Name, interfaceVar));
                sb.AppendLine(StringUtil.FormatIndent1("GetBaseInterface(&{0});", interfaceVar));
            }
            else
            {
                log.Error(StringUtil.FormatIndent0("The method [{0}] of [{1}] has no parent", theMethod.Name, theMethod.Parent.Name));
            }
            string createObject;
            StringBuilder returnFormat = FormatMethodReturn(theMethod,out createObject);
            sb.Append(returnFormat.ToString());
            string spacePrefix = "";
            if (returnFormat.Length == 0)
            {
                spacePrefix = GlobalConst.DefaultIndent;
            }
            sb.Append(spacePrefix + interfaceVar + "->");
            sb.Append(theMethod.Name + "(");
            if (theMethod.Parameters.Count > 0)
            {
                foreach (Parameter nextParam in theMethod.Parameters)
                {
                    StringBuilder paramFormat = FormatParameter(nextParam);
                    sb.Append(paramFormat.ToString());
                }
                sb.Remove(sb.Length - 1, 1);
            }
            sb.Append(")");
            sb.Append(");");
            sb.AppendLine();
            sb.Append(createObject);
            sb.AppendLine(StringUtil.FormatIndent1(GlobalConst.ComSuccessReturnValue));

            return sb.ToString();
        }

        private StringBuilder FormatParameter(Parameter param/*,out string extra*/)
        {
            StringBuilder sb = new StringBuilder();

            string paramFormat = param.Name;
            TypeMap.TypeSubstitution paramTypeSub = param.Type.Substitution.SubstitutionTypes[0];

            //PtrType ptrType = paramTypeSub.Type as PtrType;
            //InterfaceType interfaceType = paramTypeSub.Type as InterfaceType;
            //EnumType enumType = paramTypeSub.Type as EnumType;

            if (param.Attribute == ParamAttribute.InOut)
            {
                if (paramTypeSub.ContainConvertInMethod())
                {
                    paramFormat = paramTypeSub.ConvertorInMethod + "(" + param.Name + ")";
                }
            }
            else if (param.Attribute == ParamAttribute.In)
            {
                if (paramTypeSub.ContainConvertInMethod())
                {
                    paramFormat = paramTypeSub.ConvertorInMethod + "(" + param.Name + ")";
                }
            }
            else if (param.Attribute == ParamAttribute.Out)
            {
                if (paramTypeSub.ContainConvertOutMethod())
                {
                    paramFormat = paramTypeSub.ConvertorOutMethod + "(*" + param.Name + ")";
                }
            }
            sb.Append(paramFormat + ",");

            return sb;
        }

        private StringBuilder FormatMethodReturn(Method theMethod, out string createObject)
        {
            StringBuilder sb = new StringBuilder();

            createObject = "";
            if (theMethod.HasReturn())
            {
                TypeMap.TypeSubstitution retTypeSub = theMethod.ReturnType.Substitution.SubstitutionTypes[0];

                PtrType ptrType = retTypeSub.Type as PtrType;
                InterfaceType interfaceType = retTypeSub.Type as InterfaceType;
                if (ptrType != null)
                {
                    string interfaceName = Component.FindInterfaceBy(theMethod);
                    if (interfaceName.Length > 0)
                    {
                        sb.AppendLine(StringUtil.FormatIndent1("vtl::Cv_PTR retObj;"));
                        sb.Append(StringUtil.FormatIndent1("retObj = "));
                        createObject = StringUtil.FormatIndent1("CREATE_PTR_OBJECT({0},{1},{2},{3})", "retObj", "IID_" + interfaceName, "CLSID_" + interfaceName.Trim().Remove(0, 1), GlobalConst.MethodReturnVarName) + Environment.NewLine;
                    }
                }
                else if (interfaceType != null)
                {
                    string interfaceName = Component.FindInterfaceBy(theMethod);
                    if (interfaceName.Length > 0)
                    {
                        sb.AppendLine(StringUtil.FormatIndent1("{0}* pIFDOMObj = NULL;", theMethod.ReturnType.Substitution.Origin));
                        sb.Append(StringUtil.FormatIndent1("pIFDOMObj = "));
                        createObject = StringUtil.FormatIndent1("CREATE_INTERFACE_OBJECT({0},{1},{2},{3})", "pIFDOMObj", "IID_" + interfaceName, "CLSID_" + interfaceName.Trim().Remove(0, 1), GlobalConst.MethodReturnVarName) + Environment.NewLine;
                    }
                }
                else
                {
                    EnumType enumType = retTypeSub.Type as EnumType;
                    string convertPrefix = "(";
                    if (enumType != null)
                        convertPrefix = theMethod.ReturnType.Substitution.Origin + "(";
                    else if (retTypeSub.ContainConvertOutMethod())
                        convertPrefix = retTypeSub.ConvertorOutMethod + "(";

                    sb.Append(StringUtil.FormatIndent1("*" + GlobalConst.MethodReturnVarName + " = {0}", convertPrefix));
                }
            }

            return sb;
        }
    }
}
